Skip to content

aoトークンおよびサブレッジャー仕様

ステータス: DRAFT-1
対象ネットワーク: ao.TN.1

この仕様は、標準のaoトークンプロセスに必要なメッセージハンドラと機能を説明します。この標準の実装は通常、プロセスによって維持される希少性を持つ転送可能な資産を制御する能力をユーザーに提供します。

各準拠プロセスは、プロセスが表す資産の所有権を符号化するために、残高の台帳を実装することが期待されます。準拠プロセスには、通常、トークンの所有権の希少性を保証するための安全策を伴って、この台帳を変更するためのメソッドのセットがあります。

さらに、この仕様では「サブレッジャー」プロセスタイプを説明しています。これは、実装されると、親プロセスから子プロセスにトークンの数を分割移動する能力を提供します。この子プロセスは同じトークンインターフェース仕様を実装します。サブレッジャープロセスのFrom-Moduleが参加者によって信頼される場合、これらのサブレッジャーを使って「ソース」トークンで取引することができます。これは、直接メッセージを交換することなく行えます。これにより、プロセスが混雑している場合でも、参加者はそのプロセスからトークンを使用できます。オプションとして、参加者がサブレッジャープロセスが実行されているModuleを信頼している場合、これらのプロセス間での残高を代替可能として扱うことができます。この結果、任意の数の並列プロセス、およびそれに伴う取引を単一のトークンで同時に処理できます。

トークンプロセス

仕様に準拠したトークンプロセスは、さまざまな形式のメッセージに応答し、各形式はActionタグで指定されます。トークンがサポートしなければならないActionメッセージの完全なセットは以下の通りです:

NameDescriptionRead-Only
Balanceget the balance of an identifer✔️
Balancesget a list of all ledger/account balances✔️
Transfersend 1 or more units from the callers balance to one or move targets with the option to notify targets
Mintif the ledger process is the root and you would like to increase token supply

このセクションの残りでは、準拠トークンプロセスを生成するために必要なタグと、各Actionメッセージの形式およびその結果について説明します。

生成パラメータ

すべての準拠トークンプロセスは、その生成メッセージに以下の不変パラメータを含める必要があります:

TagDescriptionOptional?
NameThe title of the token, as it should be displayed to users.✔️
TickerA suggested shortened name for the token, such that it can be referenced quickly.✔️
LogoAn image that applications may deserire to show next to the token, in order to make it quickly visually identifiable.✔️
DenominationThe number of the token that should be treated as a single unit when quantities and balances are displayed to users.

メッセージングプロトコル

Balance(Target? : string)

ターゲットの残高を返します。ターゲットが指定されていない場合は、メッセージの送信者の残高を返す必要があります。

Action メッセージ:

lua
send({
    Target = "{TokenProcess Identifier}",
    Tags = {
        Action = "Balance",
        Target = "{IDENTIFIER}"
    }
})

Example response message:

{
    Tags = {
        Balance = "50",
        Target = "LcldyO8wwiGDzC3iXzGofdO8JdR4S1_2A6Qtz-o33-0",
        Ticker = "FUN"
    }
}

Balances()

トークンのすべての参加者の残高を返します。

lua
send({
    Target = "[TokenProcess Identifier]",
    Tags = {
        Action = "Balances",
        Limit = 1000, # TODO: Is this necessary if the user is paying for the compute and response?
        Cursor? = "BalanceIdentifer"
    }
})

Example response message:

lua
{
    Data = {
        "MV8B3MAKTsUOqyCzQ0Tsa2AR3TiWTBU1Dx0xM4MO-f4": 100,
        "LcldyO8wwiGDzC3iXzGofdO8JdR4S1_2A6Qtz-o33-0": 50
    }
}

Transfer(Target, Quantity)

送信者に十分な残高がある場合、QuantityTargetに送信し、受取人にCredit-Noticeを発行し、送信者にDebit-Noticeを発行します。Credit-およびDebit-Noticeは、元のTransferメッセージからすべてのタグをX-プレフィックス付きで転送する必要があります。送信者に残高が不足している場合は、失敗し、送信者に通知します。

lua
send({
    Target = "[TokenProcess Identifier]",
    Tags = {
        { name = "Action", value = "Transfer" },
        { name = "Recipient", value = "[ADDRESS]" },
        { name = "Quantity", value = "100" },
        { name = "X-[Forwarded Tag(s) Name]", value= "[VALUE]" }
    }
})

成功した転送が行われた場合、Castが設定されていない場合は通知メッセージを送信する必要があります。

lua
ao.send({
    Target = "[Recipient Address]",
    Tags = {
        { name = "Action", value = "Credit-Notice" },
        { name = "Sender", value = "[ADDRESS]" },
        { name = "Quantity", value = "100"},
        { name = "X-[Forwarded Tag(s) Name]", value= "[VALUE]" }
    }
})

受取人は、メッセージのFrom-Processタグから、どのトークンを受け取ったかを推測します。

Get-Info()

lua
send({
    Target = "{Token}",
    Tags = {
        Action = "Info"
    }
})

Mint() [optional]

Mintアクションを実装することで、プロセスは有効な参加者に新しいトークンを作成する方法を提供します。

lua
send({
    Target ="{Token Process}",
    Tags = {
        Action = "Mint",
        Quantity = "1000"
    }
})

サブレッジャープロセス

適切に機能するために、サブレッジャーはトークン契約の完全なメッセージングプロトコルを実装する必要があります(Mintアクションを除く)。サブレッジャーはまた、プロセスのために追加の機能や生成パラメータを実装する必要があります。これらの変更は、次のセクションで説明されます。

生成パラメータ

すべての準拠したサブレッジャープロセスは、その生成メッセージに以下の不変パラメータを含める必要があります:

TagDescriptionOptional?
Source-TokenThe ID of the top-most process that this subledger represents.
Parent-TokenThe ID of the parent process that this subledger is attached to.

Credit-Noticeハンドラ

Credit-Noticeメッセージを受信した際、準拠したサブレッジャープロセスは、対象のプロセスがParent-Tokenであるかどうかを確認する必要があります。もしそうであれば、サブレッジャーはSenderの残高を指定された数量だけ増加させる必要があります。

Transfer(Target, Quantity)

トークンの受取人に渡されるCredit-Noticeメッセージに含まれる通常のタグに加えて、準拠したサブレッジャープロセスは、Source-TokenParent-Tokenの両方の値も提供する必要があります。これにより、Transferメッセージの受取人は、サブレッジャープロセスのModuleを信頼している場合、Source-Tokenからの預金と同様の(代替可能な)受領をクレジットできるようになります。

修正されたCredit-Noticeは以下のように構成されるべきです:

lua
ao.send({
    Target = "[Recipient Address]",
    Tags = {
        { name = "Action", value = "Credit-Notice" },
        { name = "Quantity", value = "100"},
        { name = "Source-Token", value = "[ADDRESS]" },
        { name = "Parent-Token", value = "[ADDRESS]" },
        { name = "X-[Forwarded Tag(s) Name]", value= "[VALUE]" }
    }
})

Withdraw(Target?, Quantity)

すべてのサブレッジャーは、残高保有者がトークンを親台帳に引き出すことを許可する必要があります。Action: Withdrawメッセージを受信した際、サブレッジャーはそのParent-LedgerActionメッセージを送り、要求されたトークンを呼び出し元のアドレスに転送し、ローカルでアカウントから引き落とします。この転送により、呼び出し元に対してParent-LedgerからCredit-Noticeが発行されます。

lua
send({
    Target = "[TokenProcess Identifier]",
    Tags = {
     { name = "Action", value = "Withdraw" },
     { name = "Recipient", value = "[ADDRESS]" },
     { name = "Quantity", value = "100" }
    }
})

Token Example

NOTE: When implementing a token it is important to remember that all Tags on a message MUST be "string"s. Using thetostring function you can convert simple types to strings.

lua
if not balances then
  balances = { [ao.id] = 100000000000000 }
end

if name ~= "Fun Coin" then
  name = "Fun Coin"
end

if ticker ~= "Fun" then
  ticker = "fun"
end

if denomination ~= 6 then
  denomination = 6
end

-- handlers that handler incoming msg
handlers.add(
  "transfer",
  handlers.utils.hasMatchingTag("Action", "Transfer"),
  function (msg)
    assert(type(msg.Tags.Recipient) == 'string', 'Recipient is required!')
    assert(type(msg.Tags.Quantity) == 'string', 'Quantity is required!')

    if not balances[msg.From] then
      balances[msg.From] = 0
    end

    if not balances[msg.Tags.Recipient] then
      balances[msg.Tags.Recipient] = 0
    end

    local qty = tonumber(msg.Tags.Quantity)
    assert(type(qty) == 'number', 'qty must be number')
    -- handlers.utils.reply("Transfering qty")(msg)
    if balances[msg.From] >= qty then
      balances[msg.From] = balances[msg.From] - qty
      balances[msg.Tags.Recipient] = balances[msg.Tags.Recipient] + qty
      ao.send({
        Target = msg.From,
        Tags = {
          Action = "Debit-Notice",
          Quantity = tostring(qty)
        }
      })
      ao.send({
      Target = msg.Tags.Recipient,
      Tags = {
        Action = "Credit-Notice",
        Quantity = tostring(qty)
      }})
      -- if msg.Tags.Cast and msg.Tags.Cast == "true" then
      --   return
      -- end

    end
  end
)

handlers.add(
  "balance",
  handlers.utils.hasMatchingTag("Action", "Balance"),
  function (msg)
    assert(type(msg.Tags.Target) == "string", "Target Tag is required!")
    local bal = "0"
    if balances[msg.Tags.Target] then
      bal = tostring(balances[msg.Tags.Target])
    end
    ao.send({Target = msg.From, Tags = {
      Target = msg.From,
      Balance = bal,
      Ticker = ticker or ""
    }})
  end
)

local json = require("json")

handlers.add(
  "balances",
  handlers.utils.hasMatchingTag("Action", "Balances"),
  function (msg)
    ao.send({
      Target = msg.From,
      Data = json.encode(balances)
    })
  end

)

handlers.add(
  "info",
  handlers.utils.hasMatchingTag("Action", "Info"),
  function (msg)
    ao.send({Target = msg.From, Tags = {
      Name = name,
      Ticker = ticker,
      Denomination = tostring(denomination)
    }})
  end
)